package simpledb.record;

import simpledb.tx.Transaction;

/**
 * Manages a file of records.
 * Clients can call methods 
 * to iterate through the records
 * and access their contents.
 * @author Edward Sciore
 */
public class RecordMgr {
	private TableInfo ti;
	private Transaction tx;
	private String filename;
	private RecordPage rp;
	private int firstblknum = -1;
	private int currentblknum = -1;
	private int nextblknum;

	/**
	 * Constructs an object to manage records in a table.
	 * @param ti the table metadata
	 * @param tx the transaction
	 */
	public RecordMgr(TableInfo ti, Transaction tx) {
		this.ti = ti;
		this.tx = tx;
		filename = ti.fileName();
		firstblknum = ti.firstBlk();
		if (firstblknum == tx.size())
			appendBlock(rp);
		moveTo(firstblknum);
	}

	/**
	 * Closes the table.
	 */
	public void close() {
		rp.close();
	}

	/**
	 * Positions the current record so that a call to method next
	 * will wind up at the first record. 
	 */
	public void beforeFirst() {
		moveTo(firstblknum);
	}

	/**
	 * Moves to the next record. Returns false if there
	 * is no next record.
	 * @return false if there is no next record.
	 */
	public boolean next() {
		while (true) {
			if (rp.next())
				return true;
			if (nextblknum == -1)
				return false;
			moveTo(nextblknum);
		}
	}

	/**
	 * Returns the value of the specified field
	 * in the current record.
	 * @param fldname the name of the field
	 * @return the integer value at that field
	 */
	public int getInt(String fldname) {
		return rp.getInt(fldname);
	}

	/**
	 * Returns the value of the specified field
	 * in the current record.
	 * @param fldname the name of the field
	 * @return the string value at that field
	 */
	public String getString(String fldname) {
		return rp.getString(fldname);
	}

	/**
	 * Sets the value of the specified field 
	 * in the current record.
	 * @param fldname the name of the field
	 * @param val the new value for the field
	 */
	public void setInt(String fldname, int val) {
		rp.setInt(fldname, val);
	}

	/**
	 * Sets the value of the specified field 
	 * in the current record.
	 * @param fldname the name of the field
	 * @param val the new value for the field
	 */
	public void setString(String fldname, String val) {
		rp.setString(fldname, val);
	}

	/**
	 * Deletes the current record.
	 * The client must call next() to move to
	 * the next record.
	 * Calls to methods on a deleted record 
	 * have unspecified behavior.
	 */
	public void delete() {
		rp.delete();
	}

	/**
	 * Inserts a new, blank record somewhere in this block
	 * If the existing block can't hold it, then a new block is appended.
	 */
	public void insert() {
		int require;
		int lastblk;
		RecordPage pg = rp; 
		while ((require = rp.insert()) != 0 ) {
			if( nextblknum == -1 ){ 
				while (require-- != 0){
//					SimpleDB.bufferMgr().testShowState(SimpleDB.BLOCK_NR);
					lastblk = appendBlock(pg);
//					SimpleDB.bufferMgr().testShowState(SimpleDB.BLOCK_NR);
					pg.setNextblk(lastblk);
					if(nextblknum == -1) nextblknum = lastblk;
					if(pg != rp) pg.close();
					pg = new RecordPage(lastblk, ti, tx);
				}
				if(pg != rp) pg.close();
			}
			if(rp.currentId() == -1){
				moveTo(nextblknum);
				pg = rp;
			}
		}
	}

	/**
	 * Positions the current record as indicated by the
	 * specified RID. 
	 * @param rid a record identifier
	 */
	public void moveToRid(RID rid) {
		moveTo(rid.blockNumber());
		rp.moveToId(rid.id());
	}

	/**
	 * Returns the RID of the current record.
	 * @return a record identifier
	 */
	public RID currentRid() {
		int id = rp.currentId();
		return new RID(currentblknum, id);
	}

	private void moveTo(int b) {
		if (rp != null)
			rp.close();
		currentblknum = b;
		rp = new RecordPage(currentblknum, ti, tx);
		nextblknum = rp.getNextblk();
	}

	private int appendBlock(RecordPage p) {
		int remain, id;
		if(p != null){
			remain= p.getRemaining();
			id = p.block();
		}
		else{
			remain = 0;
			id = currentblknum;
		}
		RecordFormatter fmtr = new RecordFormatter(ti, remain, id);
		return tx.append(filename, fmtr);
	}
	
}